

#'
#' @title 简要nmf分析
#' @param dat 用于nmf分析的表达谱，一般为特征在行，样本在列的df，根据所有特征对样本进行分类
#' @param od 结果输出路径，中间所有结果保存路径
#' @param ranks nmf尝试聚类的聚类数
#' @param best_rank 最终聚类数目，可以先设置一个2或者3然后根据前面跑出来的结果来选一个合适，也可以强行指定聚类数目为2，就固定的聚为2类，但是热图可能没那么好看
#' @param seed 程序运行种子
#' @return NULL
#'
nmf_simple <- \(dat = tcga_dfs$tumor_exprs[xgene, ], od = "out/1.major_dfs/nmf/",
    ranks = 2:10, best_rank = 3, seed = 1110){

    library(NMF)
    type = 'raw'

    nmf_exprs <- as.matrix(dat)
    stopifnot(min(nmf_exprs) >= 0)

    nmf_res_for_cls <- NMF::nmf(nmf_exprs, ranks, nrun = 50, method = "brunet", seed = seed)
    dir.create(od, recursive = T, showWarnings = F)
    saveRDS(nmf_res_for_cls, file.path(od, "0.nmf根据ranks尝试聚类.rds"))

    nmf_res_for_cls <<- readRDS(file.path(od, "0.nmf根据ranks尝试聚类.rds"))
    pdf(file = file.path(od, "1.0.nmf_progress.pdf"), width = 7.5, height = 6.7,onefile = F)
    show(plot(nmf_res_for_cls))
    dev.off()

    pdf(file = file.path(od, "1.1.nmf_progress_sample_heatmap.pdf"), width = 10, height = 10,onefile = F)
    consensusmap(nmf_res_for_cls,cexRow = 0,cexCol = 0)
    dev.off()

    # 判断最佳rank值的准则就是，cophenetic 值随K变化的最大变动的前点，如3-4变化最大，所以选择最佳rank值为3.
    coph <- nmf_res_for_cls$measures$cophenetic
    coph_diff <- NULL
    for (i in 2:length(coph)) {
        coph_diff <- c(coph_diff, coph[i - 1] - coph[i])
    }
    k.best <- which.max(coph_diff) + 1

    cli::cli_alert_info(cli::style_bold(">> 自动计算best_rank {k.best}，程序运行设置为 {best_rank}"))

    # 再次NMF,rank=3
    nmf_best_rank <- nmf(nmf_exprs,
        rank = best_rank,
        nrun = 50,
        seed = seed,
        method = "brunet"
    )

    saveRDS(nmf_best_rank, file.path(od, "2.0.选用最佳聚类数后的nmf聚类结果.rds"))

    # 常见的 NMF 方法
    # Brunet (brunet):    
    # 描述: 使用多重更新规则（Multiplicative Update Rules）。
    # 应用场景: 适用于一般的 NMF 分解，尤其是基因表达数据的聚类分析。
    # 优点: 简单且易于实现，收敛速度较快。

    # Lee & Seung (lee):    
    # 描述: 采用 Lee 和 Seung 提出的乘法更新规则。
    # 应用场景: 适用于图像处理和文本挖掘等领域。
    # 优点: 理论基础扎实，广泛应用于各种数据类型。

    # KL Divergence (kl):    
    # 描述: 基于 Kullback-Leibler 散度的优化方法。
    # 应用场景: 适用于概率分布数据的分解，如文本数据的主题模型。
    # 优点: 对稀疏数据表现良好，适合处理概率分布。

    # Frobenius Norm (frobenius):    
    # 描述: 最小化 Frobenius 范数的优化方法。
    # 应用场景: 适用于一般的矩阵分解问题。
    # 优点: 简单且计算效率高。

    # Offset (offset):    
    # 描述: 在 NMF 分解中加入偏移量的优化方法。
    # 应用场景: 适用于数据中存在偏移量的情况，如基因表达数据。
    # 优点: 能够处理数据中的偏移量，提高分解的准确性。

    res_methods <- nmf(nmf_exprs, best_rank, list("lee", "brunet", "nsNMF"), nrun = 50, seed = seed)
    pdf(
      file = file.path(od, "2.1.选用最佳聚类数后的nmf聚类，方法选择heatmap，默认brunet.pdf"),
      width = 6.5, height = 6.5,onefile = F
    )
    consensusmap(res_methods, cexRow = 0, cexCol = 0)
    dev.off()

    # 可视化结果，样本聚类热图
    pdf(file.path(od, "2.2.选用最佳聚类数以及brunet后，未提取特征的nmd聚类fig.pdf"), width = 5, height = 4.5,onefile = F)
    consensusmap(nmf_best_rank,
        labRow = NA,
        labCol = NA
    )
    dev.off()

    # 提取特征
    index <- extractFeatures(nmf_best_rank, "max")
    sig.order <- unlist(index) %>% na.omit()
    nmf.input2 <- nmf_exprs[sig.order, ]

    rownames(nmf_exprs)[sig.order]

    saveRDS(rownames(nmf_exprs)[sig.order], file.path(od, "3.重要特征.rds"))

    # 用特征再做NMF
    nmf_best_rank_res <- nmf(nmf.input2,
        rank = best_rank,
        seed = seed,
        method = "brunet"
    )
    saveRDS(nmf_best_rank_res, file.path(od, "4.选用最佳聚类数以及提取重要特征后的nmf聚类结果.rds"))


    # 设置颜色
    jco <- ggsci::pal_d3()(best_rank)

    switch(type,
      raw = {
        group <- predict(nmf_best_rank) # 提出亚型

        d <- as.data.frame(group) %>%
          rownames_to_column("sample") %>%
          mutate(group = str_c("C", group)) %>%
          rename(nmf_cls = 2)
        write_tsv(d, file.path(od, "5.0.nmf_sample_cls_raw_features.tsv"))
      },
      selected = {
        group <- predict(nmf_best_rank_res) # 提出亚型

        d <- as.data.frame(group) %>%
          rownames_to_column("sample") %>%
          mutate(group = str_c("C", group)) %>%
          rename(nmf_cls = 2)
        write_tsv(d, file.path(od, "5.1.nmf_sample_cls_with_nmf_selected_feature.tsv"))
      }
    )

    # 可视化结果，样本聚类热图
    pdf(file.path(od, "6.选用最佳聚类数以及提取重要特征后的样本聚类heatmap.pdf"), width = 5, height = 4.5,onefile = F)
    consensusmap(nmf_best_rank_res,
        labRow = NA,
        labCol = NA,
        annCol = data.frame("cluster" = group[colnames(nmf_exprs)]),
        annColors = list(cluster = c("1" = jco[1], "2" = jco[2], "3" = jco[3]))
    )
    dev.off()

    names(jco) <- seq_len(best_rank)
    pdf(file.path(od, "7.选用最佳聚类数后特征heatmap.pdf"), width = 5, height = 4.5,onefile = F)
    basismap(nmf_best_rank,
        cexCol = 1,
        cexRow = 1,
        annColors = list(jco)
    )
    dev.off()
}

# # tsne可视化
# xgene_cluster <- read.delim(file.path(od, "nmf_sample_group.tsv"))
# library(M3C)
# pdf(file.path(od, "xgene_tsne.pdf"), width = 5, height = 4.5)
# tsne(train_data$data_exprs[xgene, ],
#     labels = xgene_cluster$group,
#     dotsize = 2, seed = 123, legendtitle = "Cluster"
# )
# dev.off()